// src/stores/formula.store.ts
import { defineStore } from 'pinia'
import { markRaw, reactive, shallowRef, watch } from 'vue'
import { compile, parse, type MathNode } from 'mathjs'
import * as THREE from 'three'
import { analyzeDependencies, topologicalSort } from '@/utils/math-utils'
import { createGeometry, disposeMesh } from '@/utils/three-utils'

interface Formula {
  id: string
  expression: string
  compiled?: MathNode
  dependencies: Set<string>
  mesh?: THREE.Mesh
  error?: string
  visible: boolean
}

interface Parameter {
  name: string
  value: number
  min: number
  max: number
  step: number
}

export const useFormulaStore = defineStore('formula', () => {
  const state = reactive({
    formulas: new Map<string, Formula>(),
    parameters: new Map<string, Parameter>(),
    dependencyGraph: new Map<string, Set<string>>(),
    sceneObjects: shallowRef<THREE.Object3D[]>([]),
    gridVisible: true,
    axesVisible: true,
  })

  // 数学表达式编译
  const compileExpression = (expr: string) => {
    const ast = parse(expr)
    const validSymbols = new Set([...state.parameters.keys(), 'x', 'y'])

    ast.traverse((node) => {
      if (node.type === 'SymbolNode' && !validSymbols.has(node.name)) {
        throw new Error(`未定义的变量: ${node.name}`)
      }
    })

    return compile(ast)
  }

  // 更新依赖关系
  const updateDependencies = (id: string) => {
    const formula = state.formulas.get(id)!
    const dependencies = analyzeDependencies(formula.compiled!.ast)

    // 清除旧依赖
    state.dependencyGraph.forEach((dependents) => dependents.delete(id))

    // 建立新依赖
    dependencies.forEach((depId) => {
      if (!state.dependencyGraph.has(depId)) {
        state.dependencyGraph.set(depId, new Set())
      }
      state.dependencyGraph.get(depId)!.add(id)
    })
  }

  // 重新计算所有公式
  const evaluateAll = () => {
    const evaluationOrder = topologicalSort(state.dependencyGraph)
    const scope = new Map<string, any>()

    evaluationOrder.forEach((id) => {
      const formula = state.formulas.get(id)
      if (!formula?.compiled) return

      try {
        // 创建参数作用域
        const params = Object.fromEntries(
          Array.from(state.parameters.entries()).map(([name, param]) => [
            name,
            param.value,
          ]),
        )

        // 生成几何体
        const fn = (u: number, v: number) => {
          const x = params.x ?? u * 4 - 2
          const y = params.y ?? v * 4 - 2
          return formula.compiled!.evaluate({ ...params, x, y })
        }

        const newGeometry = createGeometry(fn)
        disposeMesh(formula.mesh)
        formula.mesh = markRaw(
          new THREE.Mesh(
            newGeometry,
            new THREE.MeshPhongMaterial({
              color: new THREE.Color().setHSL(Math.random(), 0.7, 0.5),
              transparent: true,
              opacity: 0.8,
              wireframe: formula.expression.startsWith('z='),
            }),
          ),
        )

        formula.error = undefined
        scope.set(id, fn)
      }
      catch (err) {
        formula.error = err instanceof Error ? err.message : String(err)
      }
    })

    // 更新场景对象
    state.sceneObjects.value = Array.from(state.formulas.values())
      .filter((f) => f.visible && f.mesh)
      .map((f) => f.mesh!)
  }

  // 参数变化监听
  watch(
    () => Array.from(state.parameters.values()).map((p) => p.value),
    () => evaluateAll(),
    { deep: true },
  )

  return {
    formulas: state.formulas,
    parameters: state.parameters,
    sceneObjects: state.sceneObjects,
    gridVisible: state.gridVisible,
    axesVisible: state.axesVisible,

    addParameter(name: string) {
      state.parameters.set(name, {
        name,
        value: 1,
        min: -10,
        max: 10,
        step: 0.1,
      })
      evaluateAll()
    },

    addFormula(expr: string) {
      const id = `f${Date.now().toString(36)}`
      try {
        const compiled = compileExpression(expr)
        const formula: Formula = reactive({
          id,
          expression: expr,
          compiled,
          dependencies: new Set(),
          visible: true,
        })

        state.formulas.set(id, formula)
        updateDependencies(id)
        evaluateAll()
        return true
      }
      catch (err) {
        console.error('公式错误:', err)
        return false
      }
    },

    updateFormula(id: string, newExpr: string) {
      const formula = state.formulas.get(id)
      if (!formula) return

      try {
        disposeMesh(formula.mesh)
        formula.compiled = compileExpression(newExpr)
        formula.expression = newExpr
        updateDependencies(id)
        evaluateAll()
      }
      catch (err) {
        formula.error = err instanceof Error ? err.message : String(err)
      }
    },

    toggleVisibility(id: string) {
      const formula = state.formulas.get(id)
      if (formula) {
        formula.visible = !formula.visible
        evaluateAll()
      }
    },
  }
})
